﻿using System;
using System.Collections.Generic;
using System.Windows.Forms;
using System.Threading.Tasks;

using System.IO;
using System.Reflection;
using System.Text;
using System.CodeDom.Compiler;
using Microsoft.CSharp;

namespace Excuter
{
    static class Program
    {
        /// <summary>
        /// 应用程序的主入口点。
        /// </summary>
        [STAThread]
        static void Main(string[] args)
        {
            Application.EnableVisualStyles();
            Application.SetCompatibleTextRenderingDefault(false);
            //Application.Run(new Form1());

            //if (args == null || args.Length == 0) args = new string[] { @"F:\sc\桌面快捷存储\tmp\Test.cs" };
            if (args !=null && args.Length > 0)
            {
                // 其他参数作为执行参数
                string[] Arg = null;
                if (args.Length > 1)
                {
                    Arg = new string[args.Length - 1];
                    for (int i = 1; i < args.Length; i++)
                    {
                        Arg[i - 1] = args[i];
                    }
                }

                // 第一个参数作为源码或源码文件
                if (File.Exists(args[0])) Excute.RunFileFirst(args[0], Arg);
                else Excute.RunSourceCodeFirst(args[0], Arg);
            }

        }
    }

    /// <summary>
    /// 动态编译执行
    /// </summary>
    public class Excute
    {

        # region 动态编译源码并执行

        /// <summary>
        /// 解析并编译执行源码文件sourceFile，第一个类的首个公用或静态方法
        /// </summary>
        public static object RunFileFirst(string sourceFile, object[] args = null)
        {
            try
            {
                string sourceCode = fileToString(sourceFile);   
                return RunSourceCodeFirst(sourceCode, args);
            }
            catch (Exception ex)
            {
                return ex.ToString();
            }
        }

        /// <summary>
        /// 解析并编译执行sourceCode，第一个类的首个公用或静态方法
        /// </summary>
        public static object RunSourceCodeFirst(string sourceCode, object[] args = null)
        {
            try
            {
                string[] assemblies = getUsing(sourceCode).ToArray();           // 获取引用程序集
                string methodName = getFirstPublicMethod(sourceCode);           // 获取方法名
                bool isStatic = isPublicStaticMethod(sourceCode, methodName);   // 判断是否为静态方法

                return Run(sourceCode, "", methodName, args, isStatic, assemblies);    // 执行
            }
            catch (Exception ex)
            {
                return ex.ToString();
            }
        }


        /// <summary>
        /// 动态编译执行
        /// </summary>
        /// <param name="sourceCode">源码</param>
        /// <param name="classFullName">命名空间.类</param>
        /// <param name="methodName">方法名</param>
        /// <param name="args">方法参数</param>
        /// <param name="assemblies">引用程序集</param>
        /// <param name="isStaticMethod">是否为静态方法</param>
        static object Run(string sourceCode, string classFullName, string methodName, object[] args = null, bool isStaticMethod = false, string[] assemblies = null)
        {
            try
            {
                // 设置编译参数 System.Xml.dll
                CompilerParameters param = new CompilerParameters();
                param.GenerateExecutable = false;
                param.GenerateInMemory = true;

                // 添加常用的默认程序集
                param.ReferencedAssemblies.Add("Microsoft.CSharp.dll");
                param.ReferencedAssemblies.Add("mscorlib.dll");
                param.ReferencedAssemblies.Add("System.dll");
                param.ReferencedAssemblies.Add("System.Core.dll");
                param.ReferencedAssemblies.Add("System.Data.dll");
                param.ReferencedAssemblies.Add("System.Data.DataSetExtensions.dll");
                param.ReferencedAssemblies.Add("System.Drawing.dll");
                param.ReferencedAssemblies.Add("System.Windows.Forms.dll");
                param.ReferencedAssemblies.Add("System.Xml.dll");
                param.ReferencedAssemblies.Add("System.Xml.Linq.dll");

                if (assemblies != null)
                {
                    foreach (string name in assemblies)
                    {
                        string assembly = name + ".dll";
                        if (!param.ReferencedAssemblies.Contains(assembly))
                        {
                            param.ReferencedAssemblies.Add(assembly);
                        }
                    }
                }

                // 动态编译字符串代码
                CompilerResults result = new CSharpCodeProvider().CompileAssemblyFromSource(param, sourceCode);

                if (result.Errors.HasErrors)
                {
                    // 编译出错：
                    StringBuilder str = new StringBuilder();
                    foreach (CompilerError err in result.Errors)
                    {
                        str.AppendLine(err.ErrorText);
                    }
                    return str.ToString();
                }
                else
                {
                    // 编译通过：
                    Assembly assembly = result.CompiledAssembly;                // 获取已编译通过的程序集
                    if (classFullName == null || classFullName.Equals(""))      // 若未指定，则获取程序集第一个类路径名
                    {
                        classFullName = assembly.GetTypes()[0].FullName;
                    }

                    if (isStaticMethod)
                    {
                        // 调用程序集的静态方法： Type.InvokeMember
                        Type type = assembly.GetType(classFullName, true, true);

                        //object[] arg = new object[] { "参数1", "参数2" };
                        object tmp = type.InvokeMember(methodName, BindingFlags.InvokeMethod | BindingFlags.Public | BindingFlags.Static, null, null, args);
                        return tmp;
                    }
                    else
                    {
                        // 调用程序集类实例方法： method.Invoke
                        object obj = assembly.CreateInstance(classFullName);    // 创建一个类实例对象
                        MethodInfo method = obj.GetType().GetMethod(methodName);// 获取对象的对应方法

                        object tmp = method.Invoke(obj, args);                  // 调用对象的方法
                        return tmp;
                    }
                }
            }
            catch (Exception ex) 
            { 
                return ex.ToString(); 
            }
        }

        # endregion

        # region 相关功能函数

        /// <summary>
        /// 获取文件中的数据,自动判定编码格式
        /// </summary>
        private static string fileToString(String filePath)
        {
            string str = "";

            //获取文件内容
            if (File.Exists(filePath))
            {
                StreamReader file1;

                file1 = new StreamReader(filePath, Encoding.UTF8);  // 读取文件中的数据
                str = file1.ReadToEnd();                            // 读取文件中的全部数据

                file1.Close();
                file1.Dispose();
            }
            return str;
        }

        /// <summary>
        /// 获取第一个公用方法
        /// </summary>
        /// <param name="sourceCode"></param>
        /// <returns></returns>
        private static string getFirstPublicMethod(string sourceCode)
        {
            string methodName = "";
            String[] lines = sourceCode.Replace("\r\n", "\n").Split('\n');
            foreach (string iteam in lines)
            {
                string line = iteam.Trim();
                if (line.StartsWith("public ") && line.Contains("(") && line.Contains(")"))
                {
                    methodName = line.Substring(0, line.IndexOf("("));
                    methodName = methodName.Substring(methodName.LastIndexOf(" ") + 1);
                    break;
                }
            }
            return methodName;
        }

        /// <summary>
        /// 判断指定的方法是否为静态方法
        /// </summary>
        /// <returns></returns>
        private static bool isPublicStaticMethod(string sourceCode, string methodName)
        {
            bool isStatic = false;
            String[] lines = sourceCode.Replace("\r\n", "\n").Split('\n');
            foreach (string iteam in lines)
            {
                string line = iteam.Trim();
                if (line.StartsWith("public ") && line.Contains(" " + methodName) && line.Contains("(") && line.Contains(")") && line.Contains("static"))
                {
                    isStatic = true;
                }
            }
            return isStatic;
        }

        /// <summary>
        /// 获取应用的程序集信息
        /// </summary>
        private static List<string> getUsing(string sourceCode)
        {
            String[] lines = sourceCode.Replace("\r\n", "\n").Split('\n');
            List<string> usings = new List<string>();
            foreach (string iteam in lines)
            {
                string line = iteam.Trim();
                if (line.StartsWith("using ") && line.EndsWith(";"))
                {
                    string usingAssembley = line.TrimEnd(';').Substring("using ".Length);
                    CheckAddAssembly(usings, usingAssembley);
                }
            }
            return usings;
        }

        /// <summary>
        /// 检测添加较短长度的Assembly名称
        /// </summary>
        private static void CheckAddAssembly(List<string> usings, string usingAssembley)
        {
            if (usings.Contains(usingAssembley)) return;
            for (int i = 0; i < usings.Count; i++)
            {
                string name = usings[i];
                if (usingAssembley.StartsWith(name + ".")) return;
                else if (name.StartsWith(usingAssembley + "."))
                {
                    usings[i] = usingAssembley;
                }
            }
            usings.Add(usingAssembley);
        }

        # endregion

    }
}
